package com.uxsino.watcher.lib;

import com.google.common.collect.Lists;
import com.google.common.primitives.Floats;
import com.jezhumble.javasysmon.CpuTimes;
import com.jezhumble.javasysmon.JavaSysMon;
import com.uxsino.commons.utils.DateUtils;
import com.uxsino.watcher.lib.bean.Value;

import java.lang.management.*;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public class Watcher {
    public static final long BYTE_TO_KB = 1024;

    public static final long BYTE_TO_MB = BYTE_TO_KB * 1024;

    private static final long MEMORY_UNIT = BYTE_TO_MB;

    private static CpuTimes cpuCache;
    static {
        cpuCache = new JavaSysMon().cpuTimes();
    }

    public static List<Value> cpu() {
        synchronized (cpuCache) {
            List<Value> values = Lists.newArrayList();
            // CPU 信息
            JavaSysMon monitor = new JavaSysMon();
            if (cpuCache == null) {
                cpuCache = monitor.cpuTimes();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            CpuTimes cpuTimesNow = monitor.cpuTimes();
            String cpuUsage = String.format("%s", String.format("%.2f", (cpuTimesNow.getCpuUsage(cpuCache) * 100)));
            cpuCache = cpuTimesNow;

            values.add(Value.of("cpu", "CPU信息",
                Lists.newArrayList(Value.of("cpuTotalTimeMs", "运行时长", cpuCache.getTotalMillis()),
                    Value.of("cpuIdleTimeMs", "空闲时长", cpuCache.getIdleMillis()),
                    Value.of("cpuUsage", "CPU利用率", cpuUsage).suffix("%"))));
            return values;
        }
    }

    public static List<Value> jvm() {
        List<Value> values = Lists.newArrayList();
        Runtime run = Runtime.getRuntime();

         RuntimeMXBean runMxBean = ManagementFactory.getRuntimeMXBean();

        values.add(Value.of("jvm", "JVM",
            Lists.newArrayList(
                Value.of("pid", "PID", runMxBean.getName()),
                Value.of("availableProcessors", "可用处理器数", run.availableProcessors()),
                Value.of("version", "版本", System.getProperty("java.version")),
                Value.of("vendor", "供应商", System.getProperty("java.vendor")),
                Value.of("name", "VM名称", System.getProperty("java.vm.name")),
                Value.of("specificationVender", "规范版本", System.getProperty("java.specification.vender")),
                Value.of("specificationName", "规范名称", System.getProperty("java.specification.name")),
                Value.of("classVersion", "类格式版本", System.getProperty("java.class.version")),
                Value.of("classPath", "类路径", System.getProperty("java.class.path")),
                Value.of("tmpdir", "临时文件路径", System.getProperty("java.io.tmpdir")),
                Value.of("upTime", "运行时间", DateUtils.getInterval(runMxBean.getUptime()))
            )
          )
        );

        ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
        // List<Value> threadData = Lists.newArrayList();
        values.add(Value.of("thread", "线程",
            Lists.newArrayList(Value.of("daemonThreadCount", "守护线程总数", threadMxBean.getDaemonThreadCount()),
                Value.of("peakThreadCount", "线程峰值", threadMxBean.getPeakThreadCount()),
                Value.of("threadCount", "活动线程总数", threadMxBean.getThreadCount()),
                Value.of("threadAllCount", "总线程数", threadMxBean.getTotalStartedThreadCount()))));

        MemoryMXBean bean = ManagementFactory.getMemoryMXBean();
        MemoryUsage usage = bean.getHeapMemoryUsage();
        MemoryUsage nonUsage = bean.getNonHeapMemoryUsage();
        Function<Float, Boolean> fn = f -> {
            Float ZERO = 0F;
            return ZERO.compareTo(f) <= 0;
        };
        values.add(Value.of("memory", "内存", Lists.newArrayList(
            Value.of("maxMemory", "最大内存", floatUnit(run.maxMemory(), MEMORY_UNIT, fn, 0f)).suffix("MB"),// 最大可用内存
            Value.of("totalMemory", "已分配内存", floatUnit(run.totalMemory(), MEMORY_UNIT, fn, 0f)).suffix("MB"),// byte,
                                                                                                // 如果设置了最大内存，则是最大内存值，如果未设置则会根据默认参数动态变化
            Value.of("freeMemory", "剩余内存", floatUnit(run.freeMemory(), MEMORY_UNIT, fn, 0f)).suffix("MB"),
            Value.of("heapMemoryInit", "堆初始大小", floatUnit(usage.getInit(), MEMORY_UNIT, fn, 0f)).suffix("MB"),
            Value.of("heapMemoryCommitted", "堆当前分配", floatUnit(usage.getCommitted(), MEMORY_UNIT, fn, 0f)).suffix("MB"),
            Value.of("heapMemoryMax", "堆最大分配", floatUnit(usage.getMax(), MEMORY_UNIT, fn, 0f)).suffix("MB"),
            Value.of("heapMemoryUsed", "堆使用大小", floatUnit(usage.getUsed(), MEMORY_UNIT, fn, 0f)).suffix("MB"),
            Value.of("nonHeapMemoryInit", "非堆初始大小", floatUnit(nonUsage.getInit(), MEMORY_UNIT, fn, 0f)).suffix("MB"),
            Value.of("nonHeapMemoryMax", "非堆最大分配", floatUnit(nonUsage.getMax(), MEMORY_UNIT, fn, 0f)).suffix("MB"),
            Value.of("nonHeapMemoryUsed", "非堆使用大小", floatUnit(nonUsage.getUsed(), MEMORY_UNIT, fn, 0f)).suffix("MB"),
            Value.of("nonHeapMemoryCommitted", "非堆当前分配", floatUnit(nonUsage.getCommitted(), MEMORY_UNIT, fn, 0f)).suffix("MB"))));

        return values;
    }

    public static float floatUnit(float a, float unit, Function<Float, Boolean> fn, float falseVal) {
        if (fn.apply(a)) {
            return a / unit;
        } else {
            return falseVal;
        }
    }

    /**
     * 获取虚拟机的当前状态
     * @return
     */
    public static List<Value> info() {
        List<Value> info = jvm();
        info.addAll(cpu());
        return info;
    }

    public static Map<String, String> env() {
        return System.getenv();
    }

    /*public static ComponentBean componentInfo(){
    
       List<Value> values = jvm();
       ComponentBean componentBean = new ComponentBean();
    
       List<Value> memoryMsg = values.stream().filter(itm -> itm.getKey().equals("memory")).collect(Collectors.toList());
       List<Value> threadMsg = values.stream().filter(itm -> itm.getKey().equals("thread")).collect(Collectors.toList());
    
       //内存占用
       List<Value> mem = (List<Value>)memoryMsg.get(0).getValue();
       String totalMemory = mem.stream().filter(itm -> itm.getKey().equals("totalMemory"))
               .collect(Collectors.toList()).get(0).getValue().toString();
       String freeMemory = mem.stream().filter(itm -> itm.getKey().equals("freeMemory"))
               .collect(Collectors.toList()).get(0).getValue().toString();
    
       String memorycount = String.valueOf(Float.valueOf(totalMemory) - Float.valueOf(freeMemory));
    
       componentBean.setTotalMemory(totalMemory);
       componentBean.setFreeMemory(freeMemory);
       componentBean.setMemoryCount(memorycount);
    
       //当前线程数 (守护线程总数 + 活动线程总数)
       List<Value> thread = (List<Value>)threadMsg.get(0).getValue();
       String threadCount = thread.stream().filter(itm -> itm.getKey().equals("threadCount"))
               .collect(Collectors.toList()).get(0).getValue().toString();
       String daemonThreadCount = thread.stream().filter(itm -> itm.getKey().equals("daemonThreadCount"))
               .collect(Collectors.toList()).get(0).getValue().toString();
       String currentThreadCount = String.valueOf(Integer.valueOf(threadCount) + Integer.valueOf(daemonThreadCount));
    
       componentBean.setThreadCount(memorycount);
       componentBean.setDaemonThreadCount(daemonThreadCount);
       componentBean.setCurrentThreadCount(currentThreadCount);
    
       //cpu占用(%)
       componentBean.setCpuUsage(getCpuUsge());
    
       getcpuInfo(componentBean);
    
       return componentBean;
    }*/

    /*public static String getCpuUsge() {
        JavaSysMon monitor = new JavaSysMon();
        CpuTimes cpuTimes = monitor.cpuTimes();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        CpuTimes cpuTimes2 = monitor.cpuTimes();
        String cpuUsage = String.format("%.5f", cpuTimes2.getCpuUsage(cpuTimes));
    
        return cpuUsage;
    }
    
    public static void getcpuInfo(ComponentBean componentBean) {
        JavaSysMon monitor = new JavaSysMon();
    
        CpuTimes cpuTimes = monitor.cpuTimes();
        long totalTime = cpuTimes.getTotalMillis();
        long idleTime = cpuTimes.getIdleMillis();
    
        componentBean.setCpuTotalTime(String.valueOf(totalTime));
        componentBean.setCpuIdleTime(String.valueOf(idleTime));
    }*/
}
